<?php

namespace App\Http\Utils\Currency;

use App\Exceptions\Exceptions\ValidationException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\DB;
use Payment\Notify\PayNotifyInterface;

/**
 * Class TtgPay
 * @package App\Http\Utils\Currency
 */
class TtgPay extends ThirdPay
{
    const TTG = 'ttg';

    /**
     * @var string
     */
    private $url = 'https://api.tlinx.com/mct1/';

    /**
     * @var string
     */
    private $appID;

    /**
     * @var string
     */
    private $appKey;

    /**
     *
     */
    private function config($shopId)
    {
        $shopInfo = DB::table(sprintf('%sshop_info', env('DB_DATABASE_USER', '')))
            ->where('id', $shopId)
            ->first(['open_id', 'open_key']);
        if (is_null($shopInfo)) {
            //$this->appID = 'fe2eeeea64b42b80f80abe384fd0ae67';
            //$this->appKey = '1f44f20066469a7463b6660a62901128';
            throw new \Exception('该商户暂无支付渠道配置', 400);
        } else {
            $this->appID = $shopInfo->open_id;
            $this->appKey = $shopInfo->open_key;
        }
    }

    /**
     * @param int $shopId
     * @return array
     * @throws \Exception
     */
    public function payList($shopId)
    {
        $this->config($shopId);
        $data = [
            'timestamp' => time(),
            'open_id'   => $this->appID,
            'data'      => $this->encrypt(json_encode([
                'pmt_type' => 3,
            ]), $this->appKey),
        ];
        $data['sign'] = $this->signs($data, $this->appKey);
        return $this->post('paylist', $data);
    }

    /**
     * @return array
     * @throws ValidationException
     * @throws \Exception
     */
    public function pay()
    {
        if (isset($this->order->other['jump_url']) && isset($this->order->other['pmt_tag'])) {
            $this->config($this->order->shopId);
            $data = [
                'timestamp' => time(),
                'open_id'   => $this->appID,
                'data'      => $this->encrypt(json_encode([
                    'out_no'          => $this->order->orderNum,
                    'pmt_tag'         => isset($this->order->other['pmt_tag']) ? $this->order->other['pmt_tag'] : 'Cash',
                    'original_amount' => $this->order->amount,
                    'trade_amount'    => $this->order->amount,
                    'ord_name'        => $this->order->title(),
                    'jump_url'        => isset($this->order->other['jump_url']) ? $this->order->other['jump_url'] : env('APP_URL'),
                    'notify_url'      => env('TTG_NOTIFY'),
                ]), $this->appKey),
            ];
            $data['sign'] = $this->signs($data, $this->appKey);
            return $this->post('payorder', $data);
        } else {
            throw new ValidationException([
                "淘淘谷支付参数other必须包含jump_url(支付成功跳转地址)和pmt_tag，可选参数(body，title)"
            ]);
        }
    }

    /**
     * @param string $path
     * @param array $data
     * @return array
     * @throws \Exception
     */
    private function post($path, array $data)
    {
        $response = (new Client())->post($this->url . $path, [
            'form_params' => $data,
            'verify'      => false
        ]);
        $response = json_decode($response->getBody()->getContents(), true);
        if (isset($response['errcode']) && $response['errcode'] == 0) {
            $this->verify($response);
            if (isset($response['data'])) {
                return $this->decrypt($response['data'], $this->appKey);
            } else {
                throw new \Exception("TTG请求没有DATA返回");
            }
        } else {
            throw new \Exception(isset($response['msg']) ? $response['msg'] : "TTG请求错误", 400);
        }
    }

    /**
     * @param array $data
     * @throws \Exception
     */
    private function verify(array $data)
    {
        $sign = $data['sign'];
        unset($data['sign']);
        if ($this->signs($data, $this->appKey) != $sign) {
            throw new \Exception('签名验证错误');
        }
    }

    /**
     * @param string $data
     * @param string $appKey
     * @return string
     */
    private function encrypt($data, $appKey)
    {
        $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $data = $this->pkcs5pading($data, $size);
        $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $appKey, $iv);
        $data = mcrypt_generic($td, $data);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $data = strtoupper(bin2hex($data));
        return $data;
    }

    /**
     * @param string $str
     * @param string $appKey
     * @return array
     */
    private function decrypt($str, $appKey)
    {
        $str = hex2bin($str);
        $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $appKey, $str, MCRYPT_MODE_ECB);
        $padding = ord($decrypted[strlen($decrypted) - 1]);
        $decrypted = substr($decrypted, 0, -$padding);
        return json_decode($decrypted, true);
    }

    /**
     * @param string $str
     * @param int $size
     * @return string
     */
    private function pkcs5pading($str, $size)
    {
        $padding = $size - (strlen($str) % $size);
        return $str . str_repeat(chr($padding), $padding);
    }

    /**
     * @param array $data
     * @param string $appKey
     * @return string
     */
    public function signs(array $data, $appKey)
    {
        $data['open_key'] = $appKey;
        reset($data);
        ksort($data);
        return md5(sha1(urldecode(http_build_query($data))));
    }

    /**
     * @param PayNotifyInterface $notify
     * @return string
     */
    public function notify(PayNotifyInterface $notify)
    {
        if (isset($_GET['status']) && $_GET['status'] == 1) {
            $notify->notifyProcess([
                'ord_no'    => isset($_GET['ord_no']) ? $_GET['ord_no'] : null,
                'timestamp' => isset($_GET['timestamp']) ? $_GET['timestamp'] : null,
                'rand_str'  => isset($_GET['rand_str']) ? $_GET['rand_str'] : null,
                'out_no'    => isset($_GET['out_no']) ? $_GET['out_no'] : null,
                'status'    => $_GET['status'],
                'amount'    => isset($_GET['amount']) ? $_GET['amount'] : null,
                'pay_time'  => isset($_GET['pay_time']) ? $_GET['pay_time'] : null,
                'sign'      => isset($_GET['sign']) ? $_GET['sign'] : null,
                // 特殊处理
                'channel'   => self::TTG,
                'order_no'  => isset($_GET['out_no']) ? $_GET['out_no'] : null,
            ]);
        }
        return 'notify_success';
    }
}